package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.feign.BehaviorFeign;
import com.heima.article.feign.UserFeign;
import com.heima.article.mapper.*;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.article.ArticleConstans;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.dtos.ArticleSimpleInfoDto;
import com.heima.model.article.pojos.*;
import com.heima.model.article.vo.HotArticleVo;
import com.heima.model.behavior.pojos.ApBehaviorEntry;
import com.heima.model.behavior.pojos.ApLikesBehavior;
import com.heima.model.behavior.pojos.ApUnlikesBehavior;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.media.dtos.WmNewsStatisticsDto;
import com.heima.model.mess.app.ArticleVisitStreamMess;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.user.pojos.ApUserFollow;
import com.heima.utils.threadlocal.AppThreadLocalUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @作者 itcast
 * @创建日期 2021/1/31 10:38
 **/
@Service
@Slf4j
@Transactional
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
    @Autowired
    ApArticleMapper apArticleMapper;
    @Value("${fdfs.url}")
    String fileServerUrl;
//    @Value("${fdfs.oldUrl}")
//    String fileServerOldUrl;


    @Override
    public Integer findArticleCount(WmNewsStatisticsDto dto) {
        return apArticleMapper.queryArticleCountById(Long.valueOf(dto.getWmUserId()),
                dto.getStime(), dto.getEtime());
    }






    @Override
    public List<ArticleSimpleInfoDto> findArticleIdsByAuthorId(Long authorId) {
        return this.baseMapper.findArticleIdsByAuthorId(authorId);
    }




    @Override
    public ResponseResult load(Short loadtype, ArticleHomeDto dto) {
        // 1. 检查参数
        if(dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 设置分页
        if(dto.getSize() == null || dto.getSize() < 1){
            dto.setSize(10);
        }
        // 判断type是否符合要求 1   2
        if(!ArticleConstans.LOADTYPE_LOAD_MORE.equals(loadtype)&&!ArticleConstans.LOADTYPE_LOAD_NEW.equals(loadtype)){
            loadtype = ArticleConstans.LOADTYPE_LOAD_MORE;
        }
        // 判断时间参数是否符合要求
        if(dto.getMinBehotTime()==null){
            dto.setMinBehotTime(new Date());
        }
        if(dto.getMaxBehotTime()==null){
            dto.setMaxBehotTime(new Date());
        }
        // 判断频道是否符合要求
        if (dto.getTag() == null) {
            dto.setTag(ArticleConstans.DEFAULT_TAG);
        }
        List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadtype);

        ResponseResult responseResult = ResponseResult.okResult(apArticles);

        responseResult.setHost(fileServerUrl);

        return responseResult;
    }
    @Override
    public ResponseResult load2(Short loadtype, ArticleHomeDto dto, boolean firstPage) {
        // 1. 如果firstPage为true代表是第一页  可以从缓存中加载热点文章数据
        if(firstPage){
            String hotArticlesJson = stringRedisTemplate.opsForValue().get(ArticleConstans.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if(StringUtils.isNotBlank(hotArticlesJson)){
                List<HotArticleVo> hotArticleVoList = JSONArray.parseArray(hotArticlesJson, HotArticleVo.class);
                if(hotArticleVoList!=null && !hotArticleVoList.isEmpty()){
                    ResponseResult responseResult = ResponseResult.okResult(hotArticleVoList);
                    responseResult.setHost(fileServerUrl);
                    return responseResult;
                }
            }
        }
        // 2. 如果缓存没有查询到热点文章，从数据库查询文章
        return load(loadtype,dto);
    }
    @Autowired
    ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    ApArticleContentMapper apArticleContentMapper;

    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        Map<String,Object> resultMap = new HashMap<>();
        // 1. 检查参数
        if(dto == null || dto.getArticleId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 查询配置信息
        ApArticleConfig config = apArticleConfigMapper.selectOne(
                Wrappers.<ApArticleConfig>lambdaQuery()
                        .eq(ApArticleConfig::getArticleId, dto.getArticleId())
        );
        // 3. 查询文章内容信息
        if(config == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        if(!config.getIsDown()&&!config.getIsDelete()){
            ApArticleContent content = apArticleContentMapper.selectOne(
                    Wrappers.<ApArticleContent>lambdaQuery()
                            .eq(ApArticleContent::getArticleId, dto.getArticleId())
            );
            // 给文章详情内容添加图片前缀路径
            List<Map> mapList = JSONArray.parseArray(content.getContent(), Map.class);
            mapList = mapList.stream().map(m->{
                if("image".equals(m.get("type"))){
                    String imgUrl =  (String)m.get("value"); // group1/M00/00/00/sxx.jpg
                    m.put("value",fileServerUrl + imgUrl);
                }
                return m;
            }).collect(Collectors.toList());
            content.setContent(JSON.toJSONString(mapList));
            resultMap.put("content",content);
        }
        // 4. 将配置和内容都返回到前端   content   config
        resultMap.put("config",config);
        ResponseResult responseResult = ResponseResult.okResult(resultMap);
        responseResult.setHost(fileServerUrl);
        return responseResult;
    }

    @Autowired
    BehaviorFeign behaviorFeign;
    @Autowired
    ApCollectionMapper apCollectionMapper;
    @Autowired
    UserFeign userFeign;
    @Autowired
    ApAuthorMapper apAuthorMapper;


    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
        boolean isfollow = false,islike=false,isunlike=false,iscollection=false;
        // 1. 检查参数
        if(dto == null || dto.getArticleId() == null || dto.getAuthorId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 根据ID 或 设备ID查询行为实体数据  feign
        ApUser user = AppThreadLocalUtils.getUser();
        Integer userId = user == null?null:user.getId();
        ApBehaviorEntry behaviorEntry = behaviorFeign.findByUserIdOrEntryId(userId, dto.getEquipmentId());
        if(behaviorEntry == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 3. 根据ID 或 文章ID查询不喜欢行为
        ApUnlikesBehavior unlikesBehavior = behaviorFeign.findUnLikeByArticleIdAndEntryId(behaviorEntry.getId(), dto.getArticleId());
        if(unlikesBehavior!=null && unlikesBehavior.getType() == 0 ){
            isunlike = true;
        }
        // 4. 根据ID 或 文章ID查询点赞行为
        ApLikesBehavior likesBehavior = behaviorFeign.findLikeByArticleIdAndEntryId(behaviorEntry.getId(), dto.getArticleId(), ApLikesBehavior.Type.ARTICLE.getCode());
        if(likesBehavior!=null && likesBehavior.getOperation()==0){
            islike = true;
        }
        // 5. 根据ID 或 文章ID查询收藏行为
        ApCollection apCollection = apCollectionMapper.selectOne(
                Wrappers.<ApCollection>lambdaQuery()
                        .eq(ApCollection::getEntryId, behaviorEntry.getId())
                        .eq(ApCollection::getArticleId, dto.getArticleId())
                        .eq(ApCollection::getType, 0)
        );
        if(apCollection!=null){
            iscollection = true;
        }
        // 6. 根据ID 或 文章ID查关注行为
        ApAuthor apAuthor = apAuthorMapper.selectById(dto.getAuthorId());
        if(apAuthor!=null){
            ApUserFollow apUserFollow = userFeign.findByUserIdAndFollowId(userId, apAuthor.getUserId());
            if (apUserFollow !=null) {
                isfollow = true;
            }
        }
        // {"isfollow":true,"islike":true,"isunlike":false,"iscollection":true}
        Map resultMap = new HashMap();
        resultMap.put("isfollow",isfollow);
        resultMap.put("islike",islike);
        resultMap.put("isunlike",isunlike);
        resultMap.put("iscollection",iscollection);
        return ResponseResult.okResult(resultMap);
    }

    @Override
    public Map findArticle(Long id) {
        Map<String,Object> map = new HashMap();
        ApArticle apArticle = getById(id);
        ApArticleContent articleContent = apArticleContentMapper.selectOne(
                Wrappers.<ApArticleContent>lambdaQuery()
                        .eq(ApArticleContent::getArticleId, id)
        );
        map.put("id", String.valueOf(apArticle.getId()));
        map.put("publishTime", apArticle.getPublishTime());
        map.put("layout", apArticle.getLayout());
        map.put("images", apArticle.getImages());
        map.put("authorId", apArticle.getAuthorId());
        map.put("title", apArticle.getTitle());
        if(articleContent!=null){
            map.put("content",articleContent.getContent());
        }
        return map;
    }



    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {
        // 1. 根据参数中文章ID查询文章信息
        ApArticle apArticle = getById(mess.getArticleId());
        if(apArticle == null){
            log.error("更新文章热度处理失败,文章不存在 文章ID为==>{}", mess.getArticleId()+"");
            return;
        }
        // 2. 根据参数中哪个行为数据有值 更新文章对应字段的信息
        // 设置点赞 值
        apArticle.setLikes((int)(apArticle.getLikes() == null ? mess.getLike() : apArticle.getLikes() + mess.getLike()));
        // 设置阅读 值
        apArticle.setViews((int)(apArticle.getViews() == null ? mess.getView() : apArticle.getViews() + mess.getView()));
        // 设置收藏 值
        apArticle.setCollection((int)(apArticle.getCollection() == null ? mess.getCollect() : apArticle.getCollection() + mess.getCollect()));
        // 设置评论 值
        apArticle.setComment((int)(apArticle.getComment() == null ? mess.getComment() : apArticle.getComment() + mess.getComment()));
        updateById(apArticle);
        // 3. 计算文章热点得分 ， 当日文章整体权重*3
        int score = computeScore(apArticle);
        score = score * 3;
        // 4. 查询对应频道热点文章，替换热度分值较低的文章
        updateArticleCache(apArticle,score, ArticleConstans.HOT_ARTICLE_FIRST_PAGE+apArticle.getChannelId());
        // 5. 查询推荐频道热点文章, 替换热度分值较低的文章
        updateArticleCache(apArticle,score, ArticleConstans.HOT_ARTICLE_FIRST_PAGE+ArticleConstans.DEFAULT_TAG);
    }

    @Autowired
    StringRedisTemplate stringRedisTemplate;
    /**
     * 替换热度分值较低的文章
     * @param apArticle
     * @param score
     * @param cacheKey
     */
    private void updateArticleCache(ApArticle apArticle,Integer score,String cacheKey){
        // 1. 先根据缓存key查询出 热点文章集合
        String hotArticlesJson = stringRedisTemplate.opsForValue().get(cacheKey);
        if(StringUtils.isBlank(hotArticlesJson)){
            log.error("在当前缓存key中==> {} 未读取到热点文章数据",cacheKey);
            return;
        }
        List<HotArticleVo> hotArticleVoList = JSONArray.parseArray(hotArticlesJson, HotArticleVo.class);
        // 2. 判断当前文章是否已经存在热点文章中
        boolean isHas = false;
        for (HotArticleVo hotArticleVo : hotArticleVoList) {
            if(hotArticleVo.getId().equals(apArticle.getId())){  // -127  ~ 128
                // 修改 对应文章分值
                // 2.1  更新该文章热度值  重新排序
                hotArticleVo.setScore(score);
                isHas = true;
                break;
            }
        }
        // 2.2  不存在，直接将文章加入到集合中，
        if(!isHas){
            HotArticleVo hotArticleVo = new HotArticleVo();
            BeanUtils.copyProperties(apArticle,hotArticleVo);
            hotArticleVo.setScore(score);
            hotArticleVoList.add(hotArticleVo);
        }
        // 将集合重新排序(热度降序排序) 取前30热点文章
        hotArticleVoList = hotArticleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                                 .limit(30)
                                 .collect(Collectors.toList());
        // 3. 将处理之后的集合重新缓存到redis中
        stringRedisTemplate.opsForValue().set(cacheKey,JSON.toJSONString(hotArticleVoList));
    }


    private int computeScore(ApArticle apArticle) {
        // 计算每篇文章分值
        Integer score = 0;
        if(apArticle.getViews()!=null){ // 30
            score += apArticle.getViews();
        }
        if(apArticle.getLikes()!=null){ // 30
            score += apArticle.getLikes() * ArticleConstans.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if(apArticle.getComment()!=null){ // 30
            score += apArticle.getComment() * ArticleConstans.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if(apArticle.getCollection()!=null){ // 30
            score += apArticle.getCollection() * ArticleConstans.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}
